﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl.Statements;
using Weazel.Badger.Vhdl.Expressions;

#endregion

namespace Weazel.Badger
{
	/// <summary>
	/// This class represents a "hardwired" controller/datapath pair
	/// in VHDL.
	/// </summary>
  public class HardwiredEntity : ConversionEntity
  {
    private Vhdl.Process combinatorialProcess;
    private Vhdl.Process registerUpdateProcess;

		public HardwiredEntity(ConversionContext conversionContext, Gezel.Model.Controller controller)
			: base(conversionContext, controller)
    {
    }

		public override void Resolve()
		{
			base.Resolve();
		}

    public override void Convert()
    {
      if (this.Registers.Count > 0)
      {
        registerUpdateProcess = RegisterUpdateProcess.Get(this);
        AddProcess(registerUpdateProcess);
      }

      // list of signals to initialize (set 0) ..
      List<Vhdl.Signal> initializationList = new List<Weazel.Badger.Vhdl.Signal>();

      // list of signals read (should be included in the sensitivity list)
      List<Vhdl.Signal> signalsRead = new List<Weazel.Badger.Vhdl.Signal>();

      combinatorialProcess = new Weazel.Badger.Vhdl.Process("dp_combinatorial", this);
      AddProcess(combinatorialProcess);     

      // in a 'hardwired' module there should be only one sfg
      if (ConvertedSignalFlowGraphExpressions.Count != 1)
      {
        string message = "Unexpected number of signal flow graphs. Expected 1 found {0}.";
        throw new Exception(string.Format(message, ConvertedSignalFlowGraphExpressions.Count));
      }

      foreach (ExpressionConverter ec in this.ConvertedSignalFlowGraphExpressions.Values)
      {
        ec.Statements.CopyTo(combinatorialProcess.Statements);

        // add all signals created during conversion of the
        // expressions of this sfg
        foreach (Vhdl.Signal signal in ec.SignalsCreated)
        {
          AddSignal(signal);
          AddDeclarativeItem(signal);
        }

        // add all signals written to the list of signals
        // to be initialized
        foreach (Vhdl.Signal signal in ec.SignalsWritten)
          if (!initializationList.Contains(signal))
            initializationList.Add(signal);

        foreach (Vhdl.Signal signal in ec.SignalsRead)
          if (!signalsRead.Contains(signal))
            signalsRead.Add(signal);

        foreach (Vhdl.ConstantDeclaration constant in ec.Constants)
          combinatorialProcess.AddDeclarativeItem(constant);

        combinatorialProcess.Statements.InsertFirst(new Comment("initialize signals written in this process"));        
      }

      // Add signal and register initialization in front of the process
      foreach (Vhdl.Signal signal in initializationList)
      {
        if (signal is RegisterSignal)
        {
          RegisterSignal register = signal as RegisterSignal;

          // registers are initialized using their present value
          SignalAssignmentStatement stmt = new SignalAssignmentStatement(
            register.Register.WriteSignal,
            new SignalReferenceExpression(combinatorialProcess.Statements, register.Register.ReadSignal)
          );

          combinatorialProcess.Statements.InsertFirst(stmt);
        }
        else
        {
          // signals are initialized to either '0' for 1 bit types or
          // others=>'0' for other (vector) types
          SequentialStatement stmt = 
						ClearSignalStatement.Get(signal, combinatorialProcess.Statements);
          combinatorialProcess.Statements.InsertFirst(stmt);
        }
      }

      if (signalsRead.Count == 0)
      {
        combinatorialProcess.AddSignalToSensivitityList(ClockPort);
      }
      else
      {
        // make this process sensitive to all signals read
        foreach (Vhdl.Signal signal in signalsRead)
          combinatorialProcess.AddSignalToSensivitityList(signal);
      }

			if (ConversionContext.Configuration.ConvertDisplayDirectives)
				setupDisplayProcess();
    }

		private void setupDisplayProcess()
		{
			System.Diagnostics.Debug.Assert(ConvertedSignalFlowGraphExpressions.Count == 1);

			foreach (ExpressionConverter sfg in ConvertedSignalFlowGraphExpressions.Values)
			{
				StatementCollection statements = sfg.DisplayStatements;

				if (statements.Count > 0)
				{
					this.DisplayProcess.AddStatement(statements);
				}

				this.DisplayProcess.AddStatement(new EmptyLine());
			}
		}
  }
}
